Amazon CloudWatch ロググループに対して ABAC 用タグがある場合は ABAC により読み取り許可を制御して、ABAC 用タグがない場合は読み取りを許可するポリシーを試してみた
Amazon CloudWatch Logs に対してログの読み取りを ABAC で制御したいが、ABAC 用のタグが付与されていないロググループに対しては閲覧を許可したいと思い、その確認結果をブログにします。
本ブログで試したポリシーのイメージ図です。
今回は IAM ロールを利用しており、IAM ロールには ABAC 用のタグとして Project: aaa
を設定しています。Amazon CloudWatch Logs 側はタグがないロググループと Project: aaa
、Project: bbb
タグが付与されたロググループがあるとします。その場合において、Project タグがあるロググループに対しては ABAC により IAM ロールの Project タグと同じ値を持つロググループだけ読み取りでき、Project タグが付与されていないロググループは無条件で読み取りできます。
具体的な IAM ポリシーとしては、次の内容になります。ReadOnlyAccess などの CloudWatch Logs の読み取り権限があるポリシーと併用して利用する前提です。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"logs:Get*",
"logs:FilterLogEvents",
"logs:StartQuery",
"logs:StopQuery",
"logs:StartLiveTail",
"logs:StopLiveTail",
"logs:TestMetricFilter"
],
"Resource": "arn:aws:logs:*:*:log-group:*",
"Condition": {
"Null": {
"aws:ResourceTag/Project": "false"
},
"StringNotEquals": {
"aws:ResourceTag/Project": "${aws:PrincipalTag/Project}"
}
}
}
]
}
Condition の Null 条件で CloudWatch Logs ロググループに Project タグが存在していることを確認し、StringNotEquals 条件でロググループの Project タグの値と IAM ロール の Project タグの値の一致を確認します。Project タグがないロググループは Null 条件の条件に一致しないため Deny が行われません。
以降では、CloudWatch Logs のテストデータを作成して、実際に上記のポリシーの動作の一部を確認してみたいと思います。
CloudWatch Logs のテストデータ準備
テスト用に次の CloudWatch Logs のデータを作成します。キー名が「Project」のタグが ABAC に利用するタグとなります。
ロググループ名 | Project タグ |
---|---|
/test/abac/log | なし |
/test/abac/log-aaa | Project: aaa |
/test/abac/log-bbb | Project: bbb |
ロググループを作成するコマンドです。
aws logs create-log-group \
--log-group-name /test/abac/log
aws logs create-log-group \
--log-group-name /test/abac/log-aaa \
--tags Project=aaa
aws logs create-log-group \
--log-group-name /test/abac/log-bbb \
--tags Project=bbb
ログストリームとテストメッセージを作成するコマンドです。
aws logs create-log-stream \
--log-group-name /test/abac/log \
--log-stream-name test-log-stream
aws logs put-log-events \
--log-group-name /test/abac/log \
--log-stream-name test-log-stream \
--log-events "[{\"timestamp\": $(date +%s%N | cut -b1-13), \"message\": \"This is a test message\"}]"
aws logs create-log-stream \
--log-group-name /test/abac/log-aaa \
--log-stream-name test-log-aaa-stream
aws logs put-log-events \
--log-group-name /test/abac/log-aaa \
--log-stream-name test-log-aaa-stream \
--log-events "[{\"timestamp\": $(date +%s%N | cut -b1-13), \"message\": \"This is a test message\"}]"
aws logs create-log-stream \
--log-group-name /test/abac/log-bbb \
--log-stream-name test-log-bbb-stream
aws logs put-log-events \
--log-group-name /test/abac/log-bbb \
--log-stream-name test-log-bbb-stream \
--log-events "[{\"timestamp\": $(date +%s%N | cut -b1-13), \"message\": \"This is a test message\"}]"
以上で、テストデータの作成は終わります。
IAM ロールの作成
テスト用のログにアクセスする IAM ロールを作成します。
項目 | 設定値 | 備考 |
---|---|---|
ロール名 | test-abac-role | |
許可ポリシー - AWS 管理ポリシー | ReadOnlyAccess | CloudWatch Logs の読み取り権限を包含 |
許可ポリシー - カスタマー管理ポリシー | test-abac-policy | ポリシーは下記参照 |
信頼ポリシー | (記載を省略) | |
タグ | Project: aaa |
ABAC に利用 |
test-abac-policy のポリシーです。ブログの冒頭で紹介した内容と同じポリシーです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"logs:Get*",
"logs:FilterLogEvents",
"logs:StartQuery",
"logs:StopQuery",
"logs:StartLiveTail",
"logs:StopLiveTail",
"logs:TestMetricFilter"
],
"Resource": "arn:aws:logs:*:*:log-group:*",
"Condition": {
"Null": {
"aws:ResourceTag/Project": "false"
},
"StringNotEquals": {
"aws:ResourceTag/Project": "${aws:PrincipalTag/Project}"
}
}
}
]
}
Conditon の Null 条件と StringNotEquals 条件は AND 条件となっています。Null 条件で CloudWatch Logs ロググループに Project タグが存在していることを確認します。StringNotEquals 条件でロググループの Project タグの値と IAM プリンシパル(今回は IAM ロール)の Project タグの値の一致を確認します。
Null 条件と ABAC の条件式はそれぞれ次のブログの解説が参考になります。
動作確認
作成した IAM ロールにスイッチロールして事前に作成した 3 種類の CloudWatch ロググループにアクセスしてみます。
ロググループ名 | Project タグ | 想定動作 |
---|---|---|
/test/abac/log | なし | ログを閲覧できる |
/test/abac/log-aaa | Project: aaa |
ログを閲覧できる |
/test/abac/log-bbb | Project: bbb |
ログを閲覧できない |
なお、確認したアクションはログストリーム内のログの取得とインサイトの利用のみであり、テーリングとメトリクスフィルターのテストについては確認しておりません。ご注意ください。
Project タグがないロググループにアクセス
タグがないロググループである /test/abac/log のログストームを確認できました。
$ aws logs describe-log-streams --log-group-name /test/abac/log
{
"logStreams": [
{
"logStreamName": "test-log-stream",
"creationTime": 1727968080773,
"firstEventTimestamp": 1727968083104,
"lastEventTimestamp": 1727968083104,
"lastIngestionTime": 1727968084198,
"uploadSequenceToken": "49039859604571996806275410876608648010988706915264864394",
"arn": "arn:aws:logs:ap-northeast-1:111122223333:log-group:/test/abac/log:log-stream:test-log-stream",
"storedBytes": 0
}
]
}
ログストーム内のログも確認できました。
$ aws logs get-log-events --log-group-name /test/abac/log --log-stream-name test-log-stream
{
"events": [
{
"timestamp": 1727968083104,
"message": "This is a test message",
"ingestionTime": 1727968084198
}
],
"nextForwardToken": "f/38534975932495692789411082818793199302787112822650699776/s",
"nextBackwardToken": "b/38534975932495692789411082818793199302787112822650699776/s"
}
マネジメントコンソールにおいてもログの内容を確認できました。
ログのインサイトで検索することもできました。
Project: aaa タグのロググループにアクセス
Project: aaa
タグが付与されているロググループである /test/abac/log-aaa においてログストームを確認できました。
$ aws logs describe-log-streams --log-group-name /test/abac/log-aaa
{
"logStreams": [
{
"logStreamName": "test-log-aaa-stream",
"creationTime": 1727968087423,
"firstEventTimestamp": 1727968091153,
"lastEventTimestamp": 1727968091153,
"lastIngestionTime": 1727968092057,
"uploadSequenceToken": "49039859604572007252678305413435984984923135507524530828",
"arn": "arn:aws:logs:ap-northeast-1:111122223333:log-group:/test/abac/log-aaa:log-stream:test-log-aaa-stream",
"storedBytes": 0
}
]
}
ログストーム内のログも確認できました。
$ aws logs get-log-events --log-group-name /test/abac/log-aaa --log-stream-name test-log-aaa-stream
{
"events": [
{
"timestamp": 1727968091153,
"message": "This is a test message",
"ingestionTime": 1727968092057
}
],
"nextForwardToken": "f/38534976111994390892384068494515486485664399480845697024/s",
"nextBackwardToken": "b/38534976111994390892384068494515486485664399480845697024/s"
}
マネジメントコンソールにおいてもログの内容を確認できました。
ログのインサイトで検索することもできました。
Project: bbb タグのロググループにアクセス
Project: bbb
タグが付与されているロググループである /test/abac/log-bbb においてもログストームは確認できました。
$ aws logs describe-log-streams --log-group-name /test/abac/log-bbb
{
"logStreams": [
{
"logStreamName": "test-log-bbb-stream",
"creationTime": 1727968095149,
"firstEventTimestamp": 1727968097986,
"lastEventTimestamp": 1727968097986,
"lastIngestionTime": 1727968099265,
"uploadSequenceToken": "49039859604572016833753624725061899413445935812087787752",
"arn": "arn:aws:logs:ap-northeast-1:111122223333:log-group:/test/abac/log-bbb:log-stream:test-log-bbb-stream",
"storedBytes": 0
}
]
}
ログストリーム内のログは想定通り確認できませんでした。
$ aws logs get-log-events --log-group-name /test/abac/log-bbb --log-stream-name test-log-bbb-stream
An error occurred (AccessDeniedException) when calling the GetLogEvents operation: User: arn:aws:sts::111122223333:assumed-role/test-abac-role/cm-hanazawa.yuki is not authorized to perform: logs:GetLogEvents on resource: arn:aws:logs:ap-northeast-1:111122223333:log-group:/test/abac/log-bbb:log-stream:test-log-bbb-stream with an explicit deny in an identity-based policy
マネジメントコンソールでも確認してみます。
ロググループを選択することでログストリーム名までは閲覧できます。
ログストリームを選択した場合はエラーにより閲覧できません。
ログのインサイト機能も利用できないことを確認できました。
以上で動作確認は終わりです。
さいごに
Amazon CloudWatch Logs において、ABAC 用のタグがある場合のみ ABAC で読み取りを制御し、ABAC 用のタグがない場合は制限なくログを閲覧できるポリシーを試してみました。特定のロググループに重要なデータが含まれる場合に利用できるかもしれません。
また、紹介した IAM ポリシーの Null 条件がない場合は、タグのないロググループも ABAC の制御になります。すべて ABAC で制御したい場合は Deny ではなく Allow で作成する方法もあります。
以上、このブログがどなたかのご参考になれば幸いです。